#include <string>
#include "perl_loader.h"

//////////////////////////////////////////////////////////////////////////
// Constants
const wchar_t* RegSettingsKeyName = L"SOFTWARE\\GrayNM\\ScriptExt";
const wchar_t* RegSettingsPerlPath = L"PerlPath";
const wchar_t* RegSettingsPerlLib = L"PerlLibrary";

int loadResult = -1;

int LoadPerlLibrary(WindbgOutput& out, const wchar_t* module, LanguageLoader& lang)
{
	std::wstring msg(L"Perl loader at ");
	msg += module;
	msg += L"\n";
	out.Out(msg.c_str());

	CHECK_RET(lang.module = LoadLibraryW(module));
	CHECK_RET(lang.SetInterface = reinterpret_cast<script_extension::FntypeSetWindbgInterface>(
		GetProcAddress(lang.module, SCRIPTEXT_PERL_SETWINDBGINTERFACE_FN)));
	CHECK_RET(lang.RunScript = reinterpret_cast<script_extension::FntypeRunScript>(
		GetProcAddress(lang.module, SCRIPTEXT_PERL_RUNSCRIPT_FN)));

	out.Out("Success\n");
	return 0;
}

int LoadPerlFromRegistry(WindbgOutput& out, LanguageLoader& lang)
{
	RegKeyHolder reg;
	LONG res = RegOpenKeyExW(HKEY_CURRENT_USER, RegSettingsKeyName, 0, KEY_READ, &reg);
	if (res != ERROR_SUCCESS)
		return out.Err("Perl loader is not configured.\nUse !perl_config command to setup it.\n", res);
	
	// get path
	DWORD type;
	DWORD size = 0;
	CHECK_RET_RES((res = RegQueryValueExW(reg, RegSettingsPerlPath, NULL, &type, NULL, &size)) == ERROR_SUCCESS, res);
	CHECK_RET((type == REG_SZ) || (type == REG_EXPAND_SZ));
	CHECK_RET(size > 0);
	CHECK_RET((size % sizeof(wchar_t)) == 0);
	std::wstring path(size / sizeof(wchar_t), ' ');
	CHECK_RET_RES((res = RegQueryValueExW(reg, RegSettingsPerlPath, NULL, &type, reinterpret_cast<LPBYTE>(const_cast<wchar_t*>(path.data())), &size)) == ERROR_SUCCESS, res);
	path.resize(size / sizeof(wchar_t));

	std::wstring moduleName;
	MakeModuleName(path.c_str(), SCRIPTEXT_PERL_EXTENSION_NAME, moduleName);
	return LoadPerlLibrary(out, moduleName.c_str(), lang);
}

int SetPerlPath(WindbgOutput& out, const char* rootPerlPath)
{
	loadResult = -1;

	if (*rootPerlPath == 0)
	{// empty string - search for Perl automatically
	}

	std::wstring perlPath;
	int sz = MultiByteToWideChar(CP_THREAD_ACP, 0, rootPerlPath, -1, NULL, 0);
	if (sz == 0)
		return out.Err(L"Cannot convert path to Unicode", 1);
	perlPath.resize(sz - 1);
	MultiByteToWideChar(CP_THREAD_ACP, 0, rootPerlPath, -1, const_cast<LPWSTR>(perlPath.c_str()), sz);

	// parse what we have
	std::wstring path, module;
	DWORD attr = GetFileAttributesW(perlPath.c_str());
	CHECK_RET(attr != INVALID_FILE_ATTRIBUTES);
	if (attr & FILE_ATTRIBUTE_DIRECTORY)
	{
		path = perlPath;
	}
	else
	{
		std::wstring::size_type lastSlash = perlPath.find_last_of(L'\\');
		if (lastSlash != std::wstring::npos)
		{
			path = perlPath.substr(0, lastSlash + 1);
			module = perlPath.substr(lastSlash + 1);
		}
		else
		{
			module = perlPath;
		}
	}
	CHECK_RET(!path.empty());

	DWORD disp;
	RegKeyHolder reg;
	LONG res = RegCreateKeyExW(HKEY_CURRENT_USER, RegSettingsKeyName, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &reg, &disp);
	CHECK_RET_RES(res == ERROR_SUCCESS, res);
	CHECK_RET_RES((res = RegSetValueExW(reg, RegSettingsPerlPath, 0, REG_SZ, reinterpret_cast<const BYTE*>(path.c_str()), (static_cast<DWORD>(path.size()) + 1) * sizeof(wchar_t))) == ERROR_SUCCESS, res);

	return 0;
}

int LoadPerl(WindbgOutput& out, LanguageLoader& lang)
{
/*
	if (loadResult != -1)
		return loadResult;
*/

	return loadResult = LoadPerlFromRegistry(out, lang);
}